#include <stdio.h>
#include <stdlib.h>
#include <cctype>
#include <string>
#include <iostream>
#include <vector>

// Type Declarations

enum PrintBehavior {
    CloseAtSameLine,
    CloseAtOtherLine
};

/* Constant Declarations */
char      Look;                 // Lookahead Character
char      ST[26];
int       Params[26];
int       NumParams;
const int buf_len = 64;
int       DLevel  = 0;          // Debug log level

/* Print Token. Usage: PrintEnter(Token, CloseAtOtherLine/CloseAtSameLine).
   If CloseAtOtherLine, when going out of the Token name scope, you must call PrintLeave().
 */
void PrintEnter(const char* s, PrintBehavior behavior = CloseAtOtherLine) {
    for (int i = 0; i < DLevel; ++i) {
        printf("\t");
    }
    printf("<%s", s);
    if (behavior == CloseAtSameLine) {
        printf(">\n");
    }
    else {
        printf("\n");
        ++DLevel;
    }
}

void PrintLeave() {
    for (int i = 1; i < DLevel; ++i) {
        printf("\t", DLevel);
    }
    printf(">\n");
    --DLevel;
}

/* Read New Character From Input Stream */
void GetChar() {
  Look = getchar();
}

/* Report an Error */
template<typename T>
void WriteLn(const T& t) {
    std::cout << t << std::endl;
}

void Error(const std::string& s) {
  std::cout << std::endl;
  std::cout << "Error: " << s << "." << std::endl;
}

/* Report Error and Halt */
void Abort(const std::string& s) {
    Error(s);
    exit(0);
}

/* Report What Was Expected */
void Expected(const std::string& s) {
    Abort(s + " Expected");
}

// Report an Undefined Identifier
void Undefined(const std::string& n) {
    Abort("Undefined Identifier " + n);
}

// Report an Duplicate Identifier
void Duplicate(const std::string& n) {
    Abort("Duplicate Identifier " + n);
}

/* Output a String with Tab and CRLF */
void EmitLn(const std::string& s) {
  std::cout << "\t " << s << std::endl;
}

// Find the Parameter Index: Start from 1
int ParamIndex(char N) {
    return Params[N-'A'];
}

// See if an Identifier is a Parameter
bool IsParam(char N) {
    return Params[N-'A'] != 0;
}

// Add a New Parameter to Param Table
void AddParam(char Name) {
    if (IsParam(Name)) {
        Duplicate(std::string(1, Name));
    }
    NumParams++;
    Params[Name-'A'] = NumParams;
}

// Load a Parameter to the Primary Register
void LoadParam(int N) {
    int Offset = 8 + 4 * (NumParams - N);
    std::cout << "\t MOVE.L ";
    std::cout << Offset << "(A6),A0" << std::endl;
    EmitLn("MOVE (A0),D0");
}

// Store a Parameter from the Primary Register
void StoreParam(int N) {
    int Offset = 8 + 4 * (NumParams - N);
    std::cout << "\t MOVE.L " << Offset << "(A6),A0" << std::endl;
    EmitLn("MOVE D0,(A0)");
}

// Push the Primary Register to the Stack
void Push() {
    EmitLn("MOVE D0,-(SP)");
}

// Get Type of Symbol
char TypeOf(char n) {
    if (IsParam(n)) {
        return 'f';
    }
    else {
        int index = n - 'A';
        return ST[index];
    }
}

// Look for Symbol in Table
bool InTable(char n) {
    return (TypeOf(n) != 0);
}

// Add a New Entry to Symbol Table. N is variable name, T is TokenType.
void AddEntry(char N, char T) {
    if (InTable(N)) {
        Duplicate(std::string(1, N));
    }
    ST[N-'A'] = T;
}

// Check an Entry to Make Sure It's a Variable
void CheckVar(char Name) {
    if (!InTable(Name)) {
        Undefined(std::string(1, Name));
    }

    if (TypeOf(Name) != 'v') {
        Abort(std::string(1, Name) + " is not a variable");
    }
}

/* Recognize White Space */
bool IsWhite(char c) {
    return ((c == ' ') || (c == '\t'));
}

/* Skip Over Leading White Space */
void SkipWhite() {
  while (IsWhite(Look)) {
	GetChar();
  }
}

// Skip Over a Comma
void SkipComma() {
    SkipWhite();
    if (Look == ',') {
        GetChar();
        SkipWhite();
    }
}


// Skip a CRLF
void Fin() {
    while ((Look == '\n') || (Look == '\r')) {
        GetChar();
    }
    // SkipWhite();
}

/* Match a Specific Input Character */
void Match(char x) {
  if (Look == x) {
	GetChar();
  }
  else {
	char buf[buf_len] = {0};
	sprintf(buf, "'''' %c ''''", x);
	Expected(buf);
  }
  SkipWhite();
}

/* Recognize an Alpha Character  */
bool IsAlpha(char c) {
    return isalpha(toupper(c));
}

/* Recognize a Decimal Digit */
bool IsDigit(char c) {
  return isdigit(c);
}

/* Recognize an Alphanumeric Character */
bool IsAlNum(char c) {
    return (IsAlpha(c) || IsDigit(c));
}

/* Recognize an Addop */
bool IsAddop(char c) {
    return ((c == '+') || (c == '-'));
}

// Recognize a Mulop
bool IsMulop(char c) {
    return ((c == '*') || (c == '/'));
}

// Recognize a Boolean Orop
bool IsOrop(char c) {
    return ((c == '|') || (c == '~'));
}

// Recognize a Relop
bool IsRelop(char c) {
    if (c == '=' ||
        c == '#' ||
        c == '<' ||
        c == '>') {
        return true;
    }
    else {
        return false;
    }
}

/* Get an Identifier */
char GetName() {
  if (!IsAlpha(Look)) {
	Expected("Name");
  }

  char result = toupper(Look);
  GetChar();
  SkipWhite();
  return result;
}

/* Get a Number */
char GetNum() {
  if (!IsDigit(Look)) {
	Expected("Integer");
  }

  char result = Look;
  GetChar();
  SkipWhite();
  return result;
}

// Post a Label To Output
void PostLabel(const std::string& L) {
    WriteLn(L + ":");
}

// Load a Variable to Primary Register
void LoadVar(char Name) {
    CheckVar(Name);
    EmitLn(std::string("MOVE ") + Name + "(PC),D0");
}

// Store the Primary Register
void StoreVar(char Name) {
    CheckVar(Name);
    EmitLn(std::string("LEA ") + Name + "(PC),A0");
    EmitLn("MOVE D0,(A0)");
}

// Initialize Parameter Table to Null
void ClearParams() {
    for (int i = 'A'; i <= 'Z'; i++) {
        Params[i - 'A'] = 0;
    }
    NumParams = 0;
}

/* Initialize */
void Init() {
    for (int i = 0; i<26; i++) {
        ST[i] = 0;
    }

    ClearParams();
    
    GetChar();
    SkipWhite();
}

// Parse and Translate an Expression
// Vestigial Version
void Expression() {
    char Name = GetName();
    if (IsParam(Name)) {
        LoadParam(ParamIndex(Name));
    }
    else {
        LoadVar(Name);
    }
}

// Parse and Translate an Assignment Statement
void Assignment(char Name) {
    Match('=');
    Expression();
    if (IsParam(Name)) {
        StoreParam(ParamIndex(Name));
    }
    else {
        StoreVar(Name);
    }
}

// Process an Actual Parameter
void Param() {
    EmitLn("PEA " + std::string(1, GetName()) + "(PC)");
}

// Process the Parameter List for a Procedure Call
int ParamList() {
    int N = 0;
    Match('(');
    if (Look != ')') {
        Param();
        N++;
        while (Look == ',') {
            Match(',');
            Param();
            N++;
        }
    }
    Match(')');
    return 4 * N;
}

void Call(char Name) {
    EmitLn("BSR " + std::string(1, Name));
}

// Adjust the Stack Pointer Upwards by N Bytes
void CleanStack(int N) {
    if (N > 0) {
        std::cout << "ADD #" << N << ",SP" << std::endl;
    }
}

// Call a Procedure
void CallProc(char Name) {
    int N = ParamList();
    Call(Name);
    CleanStack(N);
}

// Decide if a Statement is an Assignment or Procedure Call
void AssignOrProc() {
    char Name = GetName();
    char t = TypeOf(Name);
    if (t == 0) {
        Undefined(std::string(1, Name));
    }
    else if (t == 'v' || t == 'f') {
        Assignment(Name);
    }
    else if (t == 'p') {
        CallProc(Name);
    }
    else {
        Abort("Identifier " + std::string(1, Name) + " Cannot Be Used Here");
    }
}

// Parse and Translate a Block of Statements
void DoBlock() {
    while (Look != 'e') {
        AssignOrProc();
        Fin();
    }
}

// Parse and Translate a Begin-Block
void BeginBlock() {
    Match('b');
    Fin();
    DoBlock();
    Match('e');
    Fin();
}

// Emit an RTS instruction
void Return() {
    EmitLn("RTS");
}

// Process a Formal Parameter
void FormalParam() {
    AddParam(GetName());
}

// Process the Formal Parameter List of a Procedure
void FormalList() {
    Match('(');
    if (Look != ')') {
        FormalParam();
        while (Look == ',') {
            Match(',');
            FormalParam();
        }
    }
    Match(')');
}

// Write the Prolog for a Procedure
void ProcProlog(char N) {
    PostLabel(std::string(1, N));
    EmitLn("LINK A6,#0");
}

// Write the Epilog for a Procedure
void ProcEpilog() {
    EmitLn("UNLK A6");
    EmitLn("RTS");
}

// Parse and Translate a Procedure Declaration
void DoProc() {
    char N;
    Match('p');
    N = GetName();
    FormalList();
    Fin();
    if (InTable(N)) {
        Duplicate(std::string(1, N));
    }
    ST[N-'A'] = 'p';
    ProcProlog(N);
    BeginBlock();
    ProcEpilog();
    ClearParams();
}

// Allocate Storage for a Variable
void Alloc(char N) {
    if (InTable(N)) {
        Duplicate(std::string(1, N));
    }

    ST[N-'A'] = 'v';
    WriteLn(std::string(1, N) + ":\tDC 0");
}

// Parse and Translate a Data Declaration
void Decl() {
    Match('v');
    Alloc(GetName());
}

// Forward Declarations
void DoMain();

// Parse and Translate Global Declarations
void TopDecls() {
    while (Look != '.') {
        if (Look == 'v') {
            Decl();
        }
        else if (Look == 'p') {
            DoProc();
        }
        else if (Look == 'P') {
            DoMain();
        }
        else {
            Abort("Unrecognized Keyword " + std::string(1, Look));
        }

        Fin();
    }
}

// Write the Prolog
void Prolog() {
    PostLabel("MAIN");
}

// Write the Epilog
void Epilog() {
    EmitLn("DC WARMST");
    EmitLn("END MAIN");
}

// Parse and Translate a Main Program
void DoMain() {
    char N;
    Match('P');
    N = GetName();
    Fin();
    if (InTable(N)) {
        Duplicate(std::string(1, N));
    }
    Prolog();
    BeginBlock();
}

/* Main Program */
int main(int argc, char** argv) {
  Init();
  TopDecls();
  Epilog();
  
  return 0;
}
